/*
 * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----------------------------------------------------------------------
 *
 * $Date:        1. December 2017
 * $Revision:    V2.0.0
 *
 * Project:      CMSIS-DAP Source
 * Title:        SW_DP.c CMSIS-DAP SW DP I/O
 *
 *---------------------------------------------------------------------------*/

#include "DAP_config.h"
#include "DAP.h"

// SW Macros

#define PIN_SWCLK_SET PIN_SWCLK_TCK_SET
#define PIN_SWCLK_CLR PIN_SWCLK_TCK_CLR

#define SW_CLOCK_CYCLE()                \
  PIN_SWCLK_CLR();                      \
  PIN_DELAY();                          \
  PIN_SWCLK_SET();                      \
  PIN_DELAY()

#define SW_WRITE_BIT(bit)               \
  PIN_SWDIO_OUT(bit);                   \
  PIN_SWCLK_CLR();                      \
  PIN_DELAY();                          \
  PIN_SWCLK_SET();                      \
  PIN_DELAY()

#define SW_READ_BIT(bit)                \
  PIN_SWCLK_CLR();                      \
  PIN_DELAY();                          \
  bit = PIN_SWDIO_IN();                 \
  PIN_SWCLK_SET();                      \
  PIN_DELAY()

#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)

// Generate SWJ Sequence
//   count:  sequence bit count
//   data:   pointer to sequence bit data
//   return: none
void SWJ_Sequence (uint32_t count, const uint8_t *data) {
	uint32_t val;
	uint32_t n;

	val = 0U;
	n = 0U;
	// sp("[SWJ_Sequence] <> count: %d delay: %d\r\n", count, DAP_Data.clock_delay);

	while (count--) {
		if (n == 0U) {
			val = *data++;
			n = 8U;
		}
		// printf("0x%02x ", val);
		if (val & 1U) {
			PIN_SWDIO_TMS_SET();
		} else {
			PIN_SWDIO_TMS_CLR();
		}
		SW_CLOCK_CYCLE();
		val >>= 1;
		n--;
	}
	// printf("\r\n");

}


// Generate SWD Sequence
//   info:   sequence information
//   swdo:   pointer to SWDIO generated data
//   swdi:   pointer to SWDIO captured data
//   return: none
void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
	uint32_t val;
	uint32_t bit;
	uint32_t n, k;

	n = info & SWD_SEQUENCE_CLK;
	if (n == 0U) {
		n = 64U;
	}

	if (info & SWD_SEQUENCE_DIN) {
		while (n) {
			val = 0U;
			for (k = 8U; k && n; k--, n--) {
				SW_READ_BIT(bit);
				val >>= 1;
				val  |= bit << 7;
			}
			val >>= k;
			*swdi++ = (uint8_t)val;
		}
	} else {
		while (n) {
			val = *swdo++;
			for (k = 8U; k && n; k--, n--) {
				SW_WRITE_BIT(val);
				val >>= 1;
			}
		}
	}
}

#undef  PIN_DELAY
#define PIN_DELAY() PIN_DELAY_FAST()
// SWD_TransferFunction(Fast)

#undef  PIN_DELAY
#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)
// SWD_TransferFunction(Slow)


// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
uint8_t  SWD_Transfer(uint32_t request, uint32_t *data)
{
	uint32_t ack;
	uint32_t bit;
	uint32_t val;
	uint32_t parity;

	uint32_t n;

	
	/* Packet Request */
	parity = 0U;
	SW_WRITE_BIT(1U);                     /* Start Bit */
	bit = request >> 0;
	SW_WRITE_BIT(bit);                    /* APnDP Bit */
	parity += bit;
	bit = request >> 1;
	SW_WRITE_BIT(bit);                    /* RnW Bit */
	parity += bit;
	bit = request >> 2;
	SW_WRITE_BIT(bit);                    /* A2 Bit */
	parity += bit;
	bit = request >> 3;
	SW_WRITE_BIT(bit);                    /* A3 Bit */
	parity += bit;
	SW_WRITE_BIT(parity);                 /* Parity Bit */
	SW_WRITE_BIT(0U);                     /* Stop Bit */
	SW_WRITE_BIT(1U);                     /* Park Bit */

	/* Turnaround */
	PIN_SWDIO_OUT_DISABLE();
	for (n = DAP_Data.swd_conf.turnaround; n; n--) {
		SW_CLOCK_CYCLE();
	}

	/* Acknowledge response */
	SW_READ_BIT(bit);
	ack  = bit << 0;
	SW_READ_BIT(bit);
	ack |= bit << 1;
	SW_READ_BIT(bit);
	ack |= bit << 2;
	
	// sp("request -> 0x%02x  ack -> 0x%02x\r\n", request, ack);
	
	if (ack == DAP_TRANSFER_OK) {         /* OK response */
		/* Data transfer */
		if (request & DAP_TRANSFER_RnW) {
			/* Read data */
			val = 0U;
			parity = 0U;
			for (n = 32U; n; n--) {
				SW_READ_BIT(bit);               /* Read RDATA[0:31] */
				parity += bit;
				val >>= 1;
				val  |= bit << 31;
			}
			SW_READ_BIT(bit);                 /* Read Parity */
			if ((parity ^ bit) & 1U) {
				ack = DAP_TRANSFER_ERROR;
			}
			if (data)
			{
				*data = val;
			}
			/* Turnaround */
			for (n = DAP_Data.swd_conf.turnaround; n; n--) {
				SW_CLOCK_CYCLE();
			}
			PIN_SWDIO_OUT_ENABLE();
		} else {
			/* Turnaround */
			for (n = DAP_Data.swd_conf.turnaround; n; n--) {
				SW_CLOCK_CYCLE();
			}
			PIN_SWDIO_OUT_ENABLE();
			/* Write data */
			val = *data;
			parity = 0U;
			for (n = 32U; n; n--) {
				SW_WRITE_BIT(val);              /* Write WDATA[0:31] */
				parity += val;
				val >>= 1;
			}
			SW_WRITE_BIT(parity);             /* Write Parity Bit */
		}
		/* Capture Timestamp */
		if (request & DAP_TRANSFER_TIMESTAMP) {
			DAP_Data.timestamp = TIMESTAMP_GET();
		}
		/* Idle cycles */
		n = DAP_Data.transfer.idle_cycles;
		if (n) {
			PIN_SWDIO_OUT(0U);
			for (; n; n--) {
				SW_CLOCK_CYCLE();
			}
		}
		PIN_SWDIO_OUT(1U);
		return ((uint8_t)ack);
	}

	if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
		/* WAIT or FAULT response */
		if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) {
			for (n = 32U+1U; n; n--) {
				SW_CLOCK_CYCLE();               /* Dummy Read RDATA[0:31] + Parity */
			}
		}
		/* Turnaround */
		for (n = DAP_Data.swd_conf.turnaround; n; n--) {
			SW_CLOCK_CYCLE();
		}
		PIN_SWDIO_OUT_ENABLE();
		if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) {
			PIN_SWDIO_OUT(0U);
			for (n = 32U+1U; n; n--) {
				SW_CLOCK_CYCLE();               /* Dummy Write WDATA[0:31] + Parity */
			}
		}
		PIN_SWDIO_OUT(1U);
		return ((uint8_t)ack);
	}

	/* Protocol error */
	for (n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--) {
		SW_CLOCK_CYCLE();                   /* Back off data phase */
	}
	
	PIN_SWDIO_OUT_ENABLE();
	PIN_SWDIO_OUT(1U);
	
	return ((uint8_t)ack);
}

/************************  END OF FILE ************************/

